package tournamentmanager;

import java.util.ArrayList;

public class RoundRobinTournament extends AbstractTournament {
  private static final long serialVersionUID = 2935835498692719891L;
  private static final TournamentChessEngine BYE = new TournamentChessEngine(new ChessEngine(null, null, null));
  private int matchRounds = 0;
  private int currentMatchRound = 1;

  public RoundRobinTournament() {
    this(new XboardMatchRunner());
  }

  public RoundRobinTournament(MatchRunner matchRunner) {
    setMatchRunner(matchRunner);
  }

  public void run() {
    if (matchRounds == 0) {
      runTournament();
      return;
    }
    for (int matchRound = 1; matchRound <= matchRounds; matchRound++) {
      if (currentMatchRound <= matchRound) {
        currentMatchRound = matchRound;
        runTournament();
      }
    }
  }

  private void runTournament() {
    ArrayList<TournamentChessEngine> list = new ArrayList<TournamentChessEngine>(getPlayersList());
    int numPlayers = list.size();
    if (numPlayers < 2) {
      return;
    }
    if (numPlayers % 2 == 1) {
      BYE.setPairingNumber(++numPlayers);
      list.add(BYE);
    }
    int gamesPerRound = numPlayers / 2;
    int numRounds = numPlayers - 1;
    TournamentChessEngine playerA, playerB;
    int currentGameNum = 0;
    for (int round = 1; round <= numRounds; round++) {
      for (int i = 0; i < gamesPerRound; i++) {
        playerA = list.get(i);
        playerB = list.get(numPlayers - i - 1);
        setColors(playerA, playerB, round);
        if (playerA == BYE || playerB == BYE) {
          continue;
        }
        if (getRobinGamesPlayed() < ++currentGameNum) {
          runMatch(playerA, playerB);
        }
      }
      rotate(list);
    }
  }

  private void runMatch(TournamentChessEngine playerA, TournamentChessEngine playerB) {
    MatchRunner matchRunner = getMatchRunner();
    matchRunner.setWhitePlayer(playerA);
    matchRunner.setBlackPlayer(playerB);
    if (playerA.isBlack()) {
      matchRunner.rotatePlayers();
    }
    matchRunner.setTimeControl(getTimeControl());
    matchRunner.setSaveGameFile(getOutPGNFile());
    matchRunner.setMovesPerSession(getMovesPerSession());
    matchRunner.setTimeIncrement(getTimeIncrement());
    runMatch();
  }

  void setColors(TournamentChessEngine playerA, TournamentChessEngine playerB, int round) {
    if (currentMatchRound % 2 == 0 && round == 1) {
      playerA.setColorInCurrentRound(TournamentChessEngine.BLACK);
      playerB.setColorInCurrentRound(TournamentChessEngine.WHITE);
      return;
    }
    if (round == 1 || (playerA.isWhite() && playerB.isWhite())) {
      playerA.setColorInCurrentRound(TournamentChessEngine.WHITE);
      playerB.setColorInCurrentRound(TournamentChessEngine.BLACK);
      if (playerA.getPairingNumber() > playerB.getPairingNumber()) {
        reverseColors(playerA, playerB);
      }
      return;
    }
    if (playerA.isBlack() && playerB.isBlack()) {
      playerA.setColorInCurrentRound(TournamentChessEngine.WHITE);
      playerB.setColorInCurrentRound(TournamentChessEngine.BLACK);
      if (playerA.getPairingNumber() < playerB.getPairingNumber()) {
        reverseColors(playerA, playerB);
      }
      return;
    }
    reverseColors(playerA, playerB);
  }

  private void reverseColors(TournamentChessEngine playerA, TournamentChessEngine playerB) {
    playerA.setColorInCurrentRound(!playerA.getColorInCurrentRound());
    playerB.setColorInCurrentRound(!playerB.getColorInCurrentRound());
  }

  private void rotate(ArrayList<TournamentChessEngine> list) {
    ArrayList<TournamentChessEngine> tmpList =
            new ArrayList<TournamentChessEngine>(list);
    int j = 0;
    int numPlayers = tmpList.size();
    for (int i = 0; i < numPlayers - 1; i++) {
      if (i < numPlayers / 2 - 1) {
        list.set(i, tmpList.get(numPlayers / 2 + i));
      } else {
        list.set(i, tmpList.get(j));
        j++;
      }
    }
  }

  public int getGamesToPlay() {
    if (matchRounds > 0) {
      return getGamesToPlayInSingleRound() * matchRounds;
    }
    return getGamesToPlayInSingleRound();
  }

  private int getGamesToPlayInSingleRound() {
    int n = getPlayersList().size();
    return n * (n - 1) / 2;
  }

  public void setMatchRounds(int matchRounds) {
    this.matchRounds = matchRounds * 2;
    if (this.matchRounds < 0) {
      this.matchRounds = 0;
    }
  }

  public int getMatchRounds() {
    return matchRounds;
  }

  void setCurrentMatchRound(int currentRound) {
    this.currentMatchRound = currentRound;
  }

  public int getCurrentMatchRound() {
    return currentMatchRound;
  }

  int getRobinGamesPlayed() {
    return getGamesPlayed() - (currentMatchRound - 1) * getGamesToPlayInSingleRound();
  }

  public void resetGames() {
    setCurrentMatchRound(1);
    setGamesPlayed(0);
  }
}
